#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>

/* 半委任式的ffwd */
typedef struct _cacsffwdnode{
	struct{
		APDSFUTURE_DECL_NOFLAG
		word_t flag;
		union{
			struct{
				hword_t	cid;
				apdspeff_t eff;
			};
			word_t cid_eff;
		};
	} CACHE_ALIGNED;
} _cacsffwdnode_t;

#define CACSFFWD_REQS_PERGROUPS	(16)	

#define CACSFFWD_MAX_GROUPS 	(APDS_MAX_THREADS/CACSFFWD_REQS_PERGROUPS)
#define CACSFFWD_GROUPID(tid)	(tid / (CACSFFWD_REQS_PERGROUPS - 1))
#define CACSFFWD_GROUP_TID(tid)	(tid % (CACSFFWD_REQS_PERGROUPS - 1))

typedef struct _cacsffwd {
	/* CACSFFWD_REQS_PERGROUPS - 1 是flag */
	word_t server_responses[CACSFFWD_MAX_GROUPS][CACSFFWD_REQS_PERGROUPS];
	_cacsffwdnode_t node[CACSFFWD_MAX_GROUPS*(CACSFFWD_REQS_PERGROUPS - 1)];
	lkword_t lock;
} PAGE_ALIGNED _cacsffwd_t;


DECL_COMBINE(cacsffwd)


SAI void __cacsffwd_init(_cacsffwd_t *s)
{
	// s->lock = 0;
	// s->cur_helpedgroup = 0;
	__memset(s, 0, sizeof(_cacsffwd_t));
}
SAI void *__cacsffwd_destruct(_cacsffwd_t *s){
	return (void *)s;
}

/* 提交请求 通知远程, 完成请求通知线程 */
SAI _cacsffwdnode_t *__cacsffwd_fetch(_cacsffwd_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo){
	tid_t tid = tinfo->tid;
	cid_t cur_cid = tinfo->cid;
	word_t cid_eff = new_word(APDS_GET_COREEFF(cur_cid), cur_cid);
	_cacsffwdnode_t *node = &s->node[tid];
	word_t local_flag = node->flag ^ BIT(CACSFFWD_GROUP_TID(tid)); 
	/* 参数入队 */
	apds_movargs(&node->args, args);
	/* 通知 */
	node->func = pfunc;
	node->cid_eff = cid_eff;
	/* 写入flag */
	atm_strel(&node->flag, local_flag);

	return node;
}

SAI apdsret_t __cacsffwd_wait(_cacsffwd_t *s, _cacsffwdnode_t *node, INFUN(cmbn_wait, waitpolicy), apdstinfo_t *tinfo){
	waitcnt_t cnt = -1;
	lkword_t locked = 1;
	lkword_t unlocked = 0;
	lkword_t lock;
	word_t pending;

	cid_t cur_cid = tinfo->cid;
	hword_t const help_quota = -1;
	lkword_t next_eff = 0;
	apdspeff_t cur_eff = APDS_GET_COREEFF(cur_cid);

	word_t local_flag = node->flag;
	tid_t tid = tinfo->tid;
	tid_t ingroup_offst = CACSFFWD_GROUP_TID(tid);
	word_t mask = BIT(ingroup_offst);
	word_t group_id = CACSFFWD_GROUPID(tid);
	lock = atm_ld(&s->lock);
	do {
		pending = (atm_ld(&s->server_responses[group_id][CACSFFWD_REQS_PERGROUPS - 1]) ^ local_flag) & mask;
		while ((lock > cur_eff) && pending)
		{
			cnt = waitpolicy->waitf(&s->lock, &locked, cnt);
			lock = atm_ld(&s->lock);
			pending = (atm_ld(&s->server_responses[group_id][CACSFFWD_REQS_PERGROUPS - 1]) ^ local_flag) & mask;
		}
		if (!pending){
			mp_rmb();
			return atm_ld(&s->server_responses[group_id][ingroup_offst]);
		}
		if(!(lock > cur_eff)){
			if (cas_acq_s(&s->lock, &lock, -1))
				break;
		}
	} while(TRUE);

	/* 最后一个是bits */
	word_t cached_response[CACSFFWD_REQS_PERGROUPS];
	
	extern cid_t __max_threads;
	tid_t max_tid = atm_ld(&__max_threads);
	word_t helped_group_nrs = CACSFFWD_GROUPID(max_tid) + 1; 
	
	_cacsffwdnode_t *cur;
	tid_t cur_group_tid = 0;
	tid_t cur_tid = 0;
	word_t help_cnt = 0;
	word_t ori_flag;
	word_t ret;
	/* 首先完成自己的，注意不要修改Cache行造成不必要的通讯开销 */
	word_t gidx = group_id;
	if ((atm_ld(&s->server_responses[gidx][CACSFFWD_REQS_PERGROUPS - 1]) ^ local_flag) & mask){
		ret = apds_sync_single(node->func, &node->args);
		node->flag = local_flag ^ mask;
	}
	else{
		ret = atm_ld(&s->server_responses[group_id][ingroup_offst]);
	}
	
	if(LIKELY(max_tid > 1)){
		#pragma unroll CACSFFWD_MAX_GROUPS
		for (gidx = 0; gidx < helped_group_nrs; gidx++){
			/* 每组帮助 */
			/* 获得上一次的flag */
			#ifndef HDEF_WEAK_MEMORY_ORDER
				__builtin_memcpy(&cached_response[0], &s->server_responses[gidx][0], sizeof(cached_response));
			#else
			for(tid_t tidx = 0; tidx < CACSFFWD_REQS_PERGROUPS; tidx++){
				cached_response[tidx] = s->server_responses[gidx][tidx];
			}
			#endif
			ori_flag = cached_response[CACSFFWD_REQS_PERGROUPS - 1];

			#pragma unroll CACSFFWD_REQS_PERGROUPS
			for (tid_t tidx = 0; tidx < CACSFFWD_REQS_PERGROUPS - 1; tidx++){
				/* 组内帮助 */
				cur_tid = cur_group_tid + tidx;
				cur = &s->node[cur_tid];
				/* 相反说明需要帮助 */
				if ((atm_ld(&cur->flag) ^ ori_flag) & BIT(tidx))
				{
					word_t cid_eff = atm_ld(&cur->cid_eff);
					hword_t teff = COMBINE_TEFF(cid_eff);
					/* 等于0的时候放弃 */
				

					/* 策略是留一个最大核? - 效率有点低 */
					// if (teff >= cur_eff) { 
					// 	/* 等于也算? */
					// 	/* nexteff小于的情况的情况直接continue */
					// 	if (next_eff < teff) {
					// 		next_eff = (lkword_t)teff;
					// 		/* 跳过本次帮助，待到下一轮放弃 */
					// 		continue;
					// 	}
					// 	/* next_eff不小于的情况且teff等于当前效率的情况下继续执行 */
					// 	if (teff != cur_eff) continue;
					// }
					/* 简单策略，遇到直接放弃 */
					if (UNLIKELY(!cac_next_helpquota(teff, COMBINE_TCID(cid_eff), &cur_eff, cur_cid, help_quota))) { 
						next_eff = (lkword_t)teff;
						break;
					}
					cached_response[tidx] = apds_sync_single(cur->func, &cur->args);
					/* 翻转位 */
					cached_response[CACSFFWD_REQS_PERGROUPS - 1] ^= BIT(tidx);
					help_cnt++;
				}
				/* 如果不拷贝原有结果 */
				/* else{
					cached_response[tidx] = s->server_responses[gidx][tidx];
				} */
			}
			
			if (ori_flag ^ cached_response[CACSFFWD_REQS_PERGROUPS - 1]) {
				/* 写回 */
			#ifndef HDEF_WEAK_MEMORY_ORDER
				__builtin_memcpy(&s->server_responses[gidx][0], &cached_response[0], sizeof(cached_response));
			#else
				#pragma unroll CACSFFWD_REQS_PERGROUPS
				for(tid_t tidx = 0; tidx < CACSFFWD_REQS_PERGROUPS - 1; tidx++){
					s->server_responses[gidx][tidx] = cached_response[tidx];
				}
				/* 结果写回之前确保有一次memory barrier */
				atm_strel(&s->server_responses[gidx][CACSFFWD_REQS_PERGROUPS - 1], cached_response[CACSFFWD_REQS_PERGROUPS - 1]);
				/* 是否可以改为判断优化? 不能，否则临界区结果有可能没有写回 */
			#endif
			}
			if (UNLIKELY(next_eff)) break;
			
			/* 增加 */
			cur_group_tid +=  CACSFFWD_REQS_PERGROUPS - 1;
		}
	}
RET:
	/* 释放锁 */
	atm_strel(&s->lock, next_eff);
	
	waitpolicy->wakef(&s->lock, &unlocked);
	// return atm_ld(&s->server_responses[group_id][ingroup_offst]);
	return ret;
}

PARAM_NONULL(1) SAI _cacsffwd_t *_cacsffwd_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	PARAM_USED(nodemgr);
	if (size < sizeof(_cacsffwd_t)) return NULL;
	_cacsffwd_t *f = (_cacsffwd_t *)ptr;
	__cacsffwd_init(f);
	return f;
}
PARAM_NONULL(1) SAI void *_cacsffwd_destruct(_cacsffwd_t *f, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	void *rptr = __cacsffwd_destruct(f);
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _cacsffwdnode_t *_cacsffwd_fetch(_cacsffwd_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	return __cacsffwd_fetch(f, pfunc, args, tinfo);
}
PARAM_NONULL(1, 2) SAI apdsret_t _cacsffwd_wait(_cacsffwd_t *f, _cacsffwdnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	apdsret_t ret = __cacsffwd_wait(f, node, waitpolicy, tinfo);
	return ret;
}
